/*
 * @Author: wangjun haodreams@163.com
 * @Date: 2024-12-28 11:42:37
 * @LastEditors: wangjun haodreams@163.com
 * @LastEditTime: 2025-11-13 16:00:05
 * @FilePath: \libs\csvx\csvx.go
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
package csvx

import (
	"bufio"
	"bytes"
	"errors"
	"os"
	"reflect"
	"strings"

	"gitee.com/haodreams/libs/easy"
)

type InvalidUnmarshalError struct {
	Type reflect.Type
}

func (e *InvalidUnmarshalError) Error() string {
	if e.Type == nil {
		return "csv: Unmarshal(nil)"
	}

	if e.Type.Kind() != reflect.Pointer {
		return "csv: Unmarshal(non-pointer " + e.Type.String() + ")"
	}
	return "csv: Unmarshal(nil " + e.Type.String() + ")"
}

func  UnmarshalFile(filename string, v any) error {
	data,err:= os.ReadFile(filename)
	if err != nil{
		return err
	}
	return Decode(bufio.NewReader(bytes.NewBuffer(data)), v)
}

func Unmarshal(data []byte, v any) error {
	return Decode(bufio.NewReader(bytes.NewBuffer(data)), v)
}

func Marshal(v any) ([]byte, error) {
	return marshal(v)
}

func Load(file string, v any) (err error) {
	f, err := os.Open(file)
	if err != nil {
		return
	}
	defer f.Close()
	return Decode(bufio.NewReader(f), v)
}

func Save(file string, v any) (err error) {
	tmpFile := file + ".tmp"
	f, err := os.Create(tmpFile)
	if err != nil {
		return
	}
	buf := bufio.NewWriter(f)
	Encode(v, buf)
	buf.Flush()
	f.Close()
	_, err = os.Stat(file)
	if err == nil {
		err = os.Remove(file)
		if err != nil {
			return
		}
	}
	err = os.Rename(tmpFile, file)
	return
}

func marshal(v any) ([]byte, error) {
	buf := bytes.NewBuffer(nil)
	writer := bufio.NewWriter(buf)
	Encode(v, writer)
	writer.Flush()
	return buf.Bytes(), nil
}

func Encode(v any, buf *bufio.Writer) {
	var titles []string
	var paths [][]int
	easy.NameID(reflect.TypeOf(v), &titles, &paths, nil)
	for i := range titles {
		if i > 0 {
			buf.WriteByte(',')
		}
		buf.WriteString(titles[i])
	}
	buf.WriteByte('\n')
	num := len(titles)

	val := reflect.ValueOf(v)
	if val.Type().Kind() == reflect.Ptr {
		val = val.Elem()
	}
	kind := val.Type().Kind()
	if kind == reflect.Slice || kind == reflect.Array {
		n := val.Len()
		for i := 0; i < n; i++ {
			field := val.Index(i)
			if field.Kind() == reflect.Ptr {
				field = field.Elem()
			}
			for j := 0; j < num; j++ {
				if j > 0 {
					buf.WriteByte(',')
				}
				vv := field.FieldByIndex(paths[j])
				buf.WriteString(easy.ValueToString(vv, EscapeEncode))
			}
			buf.WriteByte('\n')
		}
		return
	}
	for j := 0; j < num; j++ {
		if j > 0 {
			buf.WriteByte(',')
		}
		vv := val.FieldByIndex(paths[j])
		buf.WriteString(easy.ValueToString(vv, EscapeEncode))
	}
	buf.WriteByte('\n')
}

func Decode(r *bufio.Reader, slice any) (err error) {
	v := reflect.ValueOf(slice)
	if v.Kind() != reflect.Pointer || v.IsNil() {
		return &InvalidUnmarshalError{reflect.TypeOf(slice)}
	}
	v = reflect.Indirect(v)
	//v = v.Elem()
	if v.Kind() != reflect.Slice {
		return errors.New("参数必须是结构化数组地址")
	}

	var titles []string
	var paths [][]int
	dict := map[string][]int{}
	easy.NameID(v.Type(), &titles, &paths, nil)
	for i := range titles {
		dict[titles[i]] = paths[i]
	}

	line, err := r.ReadString('\n')
	if err != nil {
		return
	}
	line = strings.TrimSpace(line)
	ss := strings.Split(line, ",")
	nColumn := len(ss)
	colunIdx := make([][]int, nColumn)
	for i := 0; i < nColumn; i++ {
		title := strings.TrimSpace(ss[i])
		colunIdx[i] = dict[title]
	}
	lines := make([]string, 0, 100)
	for line, err := r.ReadString('\n'); err == nil || len(line) > 0; line, err = r.ReadString('\n') {
		line = strings.TrimRightFunc(line, func(r rune) bool {
			return r == '\r' || r == '\n'
		})
		lines = append(lines, line)
	}
	lineSize := len(lines)
	v.Set(reflect.MakeSlice(v.Type(), lineSize, lineSize))
	v.SetLen(lineSize)
	idx := 0
	for i := range lines {
		if strings.TrimSpace(lines[i]) == "" {
			continue
		}
		ss := strings.Split(lines[i], ",")
		n := len(ss)
		if n > nColumn {
			n = nColumn
		}
		field := v.Index(idx)
		if field.Kind() == reflect.Ptr {
			field.Set(reflect.New(field.Type().Elem()))
			field = field.Elem()
		}
		for j := 0; j < n; j++ {
			idx := colunIdx[j]
			if idx == nil {
				continue
			}
			easy.ValueFromString(ss[j], field.FieldByIndex(idx), EscapeDecode)
		}
		idx++
	}
	v.SetLen(idx)
	return
}

func EscapeEncode(s string) string {
	return strings.ReplaceAll(s, ",", "&#44;")
}
func EscapeDecode(s string) string {
	return strings.ReplaceAll(s, "&#44;", ",")
}

//编译一行
func EncodeLine(s string)string{
	s = strings.ReplaceAll(s, "\r", "&#13;")
	return  strings.ReplaceAll(s, "\n", "&#10;")
}

//解码一行
func DecodeLine(s string)string{
	s = strings.ReplaceAll(s, "&#13;", "\r")
	return  strings.ReplaceAll(s, "&#10;", "\n")
}